/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Vector2_h__
#define __dylab_Vector2_h__

//===========================================================================================
#include <dylab/utils/math/Math.h>
#include <dylab/utils/EnumIterator.h>
#include <dylab/utils/Array.h>
#include <dylab/utils/types/TypeParser.h>

//===========================================================================================
namespace dylab {

    /** 
     *
     */
    class DYLAB_EXPORT Vector2 
    {
    public:
        ///
        enum elementidx_t
        {
            E_0 = 0,
            E_1
        };
        ///
        typedef EnumIterator<elementidx_t, E_0, E_1> ElementIdxIterator;
        ///
        enum { E_COUNT = E_1 + 1 };
    public:
        /// zero and unit vector constants
        static const Vector2 ZERO;
        static const Vector2 ONE;
        static const Vector2 UNIT_X;
        static const Vector2 UNIT_Y;
        ///
        static Vector2 random(const Vector2 & _exclude = ZERO);
    protected:       
        /// the point's coordinates        
        union 
        {
            struct { real_t mX, mY; };
            real_t mElements[E_COUNT];
        };
    public:

        //-----------------------------------------------------------------------------
        // constructors
        //-----------------------------------------------------------------------------

        Vector2() 
                { }

        Vector2(real_t _x, real_t _y) 
            : mX(_x), mY(_y) 
                { }

        Vector2(const Vector2 & _v) 
            : mX(_v.mX), mY(_v.mY)
                { }

        Vector2(const ArrayReal<2> & _v)
            : mX(_v[0]), mY(_v[1])
                { } 

        //-----------------------------------------------------------------------------
        // attribut access
        //-----------------------------------------------------------------------------

        /** accesss the point's _x coordinate
         */
        real_t & x() 
                { return mX; }

        /** accesss the point's _x coordinate, without premission to modify it
         */
        const real_t & x() const
                { return mX; }

        /** accesss the point's _y coordinate
         */
        real_t & y() 
                { return mY; }

        /** accesss the point's _y coordinate, without premission to modify it
         */
        const real_t & y() const
                { return mY; }

        /**
        */
        real_t & operator[] (elementidx_t _elementIdx) 
                { return * (&mX + _elementIdx); }

        /**
        */
        const real_t & operator[] (elementidx_t _elementIdx) const
                { return * (&mX + _elementIdx); }

        /**
        */
        real_t & operator[] (uint32_t _elementIdx) 
                { 
                    DYLAB_ASSERT(_elementIdx < E_COUNT);
                    return * (&mX + _elementIdx); 
                }

        /**
        */
        const real_t & operator[] (uint32_t _elementIdx) const
                { 
                    DYLAB_ASSERT(_elementIdx < E_COUNT);
                    return * (&mX + _elementIdx); 
                }

        //-----------------------------------------------------------------------------
        // properties
        //-----------------------------------------------------------------------------

        /**
        */
        bool isDegenerated() const
                { return Math::almostZero(length()); }

        //-----------------------------------------------------------------------------
        // manipulators
        //-----------------------------------------------------------------------------

        /**
        */
        Vector2 inverse() const
                { return Vector2(-mX, -mY); }

        /**
        */
        Vector2 & add(const Vector2 & _v) 
                { mX += _v.mX, mY += _v.mY; return *this; }

        /**
        */
        Vector2 & add(real_t _x) 
                { return add(Vector2(_x, _x)); }
        
        /**        
         */
        Vector2 & sub(const Vector2 & _v) 
                { mX -= _v.mX, mY -= _v.mY; return *this; }

        /**        
         */
        Vector2 & sub(real_t _x) 
                { return sub(Vector2(_x, _x)); }

        /**
        */
        Vector2 & mul(real_t _x) 
                { mX *= _x, mY *= _x; return *this; }

        /**        
         */
        Vector2 & div(real_t _x) 
                { mX /= _x, mY /= _x; return *this; }

        /**
        */
        real_t dot(const Vector2 & _v) const
                { return mX * _v.mX + mY * _v.mY; }

        /**
        */
        real_t length() const 
                { return Math::squareRoot(dot(*this)); }

        /**
        */
        real_t sum() const
                { return mX + mY; }

        /**
        */
        Vector2 & normalise() 
                { real_t len = length(); if (len > 0) div(len); return *this; }

        /**
        */
        void clamp(const Vector2 & _min, const Vector2 & _max)
                { 
                    mX = mX < _min.mX ? _min.mX : mX; 
                    mY = mY < _min.mY ? _min.mY : mY; 
                    mX = mX > _max.mX ? _max.mX : mX; 
                    mY = mY > _max.mY ? _max.mY : mY; 
                }

        /**
        */
        bool almostEqual(const Vector2 & _v, real_t _tolerance = Math::EQUALITY_TOLERANCE) const
                { return Math::almostEqual(mX, _v.mX, _tolerance) && Math::almostEqual(mY, _v.mY, _tolerance); }

        /**
        */
        bool almostZero(real_t _tolerance = Math::EQUALITY_TOLERANCE) const
                { return almostEqual(ZERO, _tolerance); }

        //-----------------------------------------------------------------------------
        // operators
        //-----------------------------------------------------------------------------

        /**
        */
        Vector2 operator - () const
                { return inverse(); }

        /**
        */
        Vector2 operator + (const Vector2 & _v) const
                { return Vector2(mX + _v.mX, mY + _v.mY); }

        /**
        */
        Vector2 operator - (const Vector2 & _v) const
                { return Vector2(mX - _v.mX, mY - _v.mY); }

        /**
        */
        Vector2 operator * (real_t _x) const
                { return Vector2(mX * _x, mY * _x); }

        /**
        */
        Vector2 operator / (real_t _x) const
                { return Vector2(mX / _x, mY / _x); }

        /**
        */
        Vector2 & operator = (const Vector2 & _v) 
                { mX = _v.mX, mY = _v.mY; return *this; }

        /**
        */
        Vector2 & operator += (const Vector2 & _v) 
                { return add(_v); }

        /**
        */
        Vector2 & operator += (real_t _x) 
                { return add(_x); }

        /**
        */
        Vector2 & operator -= (const Vector2 & _v) 
                { return sub(_v); }

        /**
        */
        Vector2 & operator -= (real_t _x) 
                { return sub(_x); }

        /**
        */
        Vector2 & operator *= (real_t _x) 
                { return mul(_x); }

        /**
        */
        Vector2 & operator /= (real_t _x) 
                { return div(_x); }

        /**
        */
        bool operator == (const Vector2 & _v) const
                { return (mX == _v.mX) && (mY == _v.mY); }

        /**
        */
        bool operator != (const Vector2 & _v) const
                { return (mX != _v.mX) || (mY != _v.mY); }

        /**
        */
        bool operator > (const Vector2 & _v) const
                { return (mX > _v.mX) && (mY > _v.mY); }

        /**
        */
        bool operator >= (const Vector2 & _v) const
                { return (mX >= _v.mX) && (mY >= _v.mY); }

        /**
        */
        bool operator < (const Vector2 & _v) const
                { return (mX < _v.mX) && (mY < _v.mY); }

        /**
        */
        bool operator <= (const Vector2 & _v) const
                { return (mX <= _v.mX) && (mY <= _v.mY); }

        //-----------------------------------------------------------------------------
        // conversion
        //-----------------------------------------------------------------------------

        operator String() const                
                { return String::fmt("%1% %2%", mX, mY); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const Vector2 & _o)
            { return _os << String(_o); }

    //===========================================================================================
    template <>
        inline Vector2 TypeParser::parse(const String & _string)
            { return Vector2(ArrayReal<2>::parse(_string)); }
    template <>
        inline Vector2 TypeParser::parse(const String & _string, const Vector2 & _defaultValue)
            {
                try { return TypeParser::parse<Vector2>(_string); }
                catch (EInvalidFormat &) { return _defaultValue; }
            }
}

//===========================================================================================
#endif // __dylab_Vector2_h__
